package com.yk.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yk.common.core.constant.CacheConstants;
import com.yk.common.core.constant.NumberConstant;
import com.yk.common.core.exception.ServiceException;
import com.yk.common.redis.service.RedisService;
import com.yk.system.entity.Message;
import com.yk.system.entity.Notification;
import com.yk.system.entity.User;
import com.yk.system.mapper.NotificationMapper;
import com.yk.system.service.MessageService;
import com.yk.system.service.NotificationService;
import com.yk.system.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 系统通知 服务类
 *
 * @author lmx
 * @since 2024-01-05
 */
@Service
@RequiredArgsConstructor
public class NotificationServiceImpl extends ServiceImpl<NotificationMapper, Notification> implements NotificationService {

    private final RedisService redisService;
    private final UserService userService;
    private final MessageService messageService;

    @Override
    public void saveNotification(Notification notification) {
        Assert.notNull(notification, "通知对象不能为空");
        Assert.notNull(notification.getSendAt(), "发送时间不能为空");
        Date now = new Date();
        if (now.after(notification.getSendAt())) {
            throw new ServiceException("不能选择已过期的时间");
        }
        long id = IdWorker.getId();
        notification.setId(id);
        // 计算过期时间
        long time = DateUtil.between(now, notification.getSendAt(), DateUnit.MINUTE);
        redisService.setCacheObject(CacheConstants.NOTIFICATION_EXPIRED_TIME_KEY + id, id, time, TimeUnit.MINUTES);
        baseMapper.insert(notification);
    }

    @Override
    public void updateNotification(Notification notification) {
        Assert.notNull(notification, "通知对象不能为空");
        Assert.notNull(notification.getSendAt(), "发送时间不能为空");
        Date now = new Date();
        if (now.after(notification.getSendAt())) {
            throw new ServiceException("不能选择已过期的时间");
        }
        // 计算过期时间
        long time = DateUtil.between(now, notification.getSendAt(), DateUnit.MINUTE);
        redisService.setCacheObject(CacheConstants.NOTIFICATION_EXPIRED_TIME_KEY + notification.getId(), notification.getId(), time, TimeUnit.MINUTES);
        baseMapper.updateById(notification);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void sendMessage(String id) {
        Notification notification = baseMapper.selectById(id);
        if (Objects.isNull(notification)) {
            return;
        }
        List<User> list = userService.list(new LambdaQueryWrapper<User>().eq(User::getStatus, NumberConstant.ZERO_STR));
        if (CollUtil.isEmpty(list)) {
            return;
        }
        List<Message> messageList = list.stream().map(user -> {
            Message message = new Message();
            message.setId(IdWorker.getId());
            message.setUserId(user.getId());
            message.setTitle(notification.getTitle());
            message.setContent(notification.getContent());
            message.setType(NumberConstant.ZERO_STR);
            message.setTargetType(NumberConstant.ZERO_STR);
            message.setCreatedBy(1L);
            return message;
        }).collect(Collectors.toList());
        //只回滚以下异常，
        Object savePoint = TransactionAspectSupport.currentTransactionStatus().createSavepoint();
        try {
            messageService.batchInsert(messageList);
            notification.setStatus(NumberConstant.ONE_STR);
            baseMapper.updateById(notification);
        } catch (Exception e) {
            //手工回滚异常
            TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savePoint);
            notification.setStatus(NumberConstant.TWO_STR);
            baseMapper.updateById(notification);
        }
    }
}